//@author A0101119Y
#include "DbMgr.h"

using namespace Twentyfifth;
using namespace std;

DbMgr::DbMgr() {
	FILENAME_TASKS = FILE_DATABANK;
	FileReader reader(FILE_DATABANK);
	tasks = reader.readTasksFromFile();
	incrementalCounter = getLargestTaskId();
}

DbMgr::DbMgr(string filename) {
	FILENAME_TASKS = filename;
	FileReader reader(filename);
	tasks = reader.readTasksFromFile();
	incrementalCounter = getLargestTaskId();
}

DateTime DbMgr::swapDtFields(DateTime oldDt, DateTime newDt, TaskParams param) {
	switch (param) {
	case DATE_DEADLINE:
	case DATE_END:
	case DATE_START:
		oldDt.setDay(newDt.getDay());
		oldDt.setMonth(newDt.getMonth());
		oldDt.setYear(newDt.getYear());
		break;
	case TIME_DEADLINE:
	case TIME_END:
	case TIME_START:
		oldDt.setHour(newDt.getHour());
		oldDt.setMinute(newDt.getMinute());
		break;
	default:
		break;
	}
	return oldDt;
}

bool DbMgr::isInDatabase(int id) {
	for (vector<Task>::iterator it = tasks.begin(); it != tasks.end(); it++) {
		if ((*it).getId() == id) {
			return true;
		}
	}
	return false;
}

Status DbMgr::addTask(Task t) {
	DateTime dtUpdated;
	dtUpdated.setToNow();
	int id = t.getId();
	if (id != 0) {
		if (findTaskIdInVector(id) != -1) {
			return Status(Status::ERR_OTHER, "ID already exists.");
		}
	} else {
		do {
			incrementalCounter++;
		} while (findTaskIdInVector(incrementalCounter) != -1);
		t.setId(incrementalCounter);
	}
	t.setLastUpdated(dtUpdated);
	tasks.push_back(t);
	writeAllToFile();
	Status s;
	s.tasks.push_back(t);
	s.code = Status::OK;
	return s;
}

Status DbMgr::editTask(int id, TaskParams param, DateTime dt) {
	DateTime dtUpdated;
	dtUpdated.setToNow();
	enum DtType { DEADLINE, START, END };
	DtType type;
	switch (param) {
	case DATE_DEADLINE:
	case TIME_DEADLINE:
		type = DEADLINE;
		break;
	case DATE_START:
	case TIME_START:
		type = START;
		break;
	case DATE_END:
	case TIME_END:
		type = END;
		break;
	default:
		return Status(Status::ERR_INVALID_COMMAND);
		break;
	}
	for (vector<Task>::iterator it = tasks.begin(); it != tasks.end(); it++) {
		if ((*it).getId() == id) {
			if ((*it).getDeleted() == false) {
				(*it).setFullDay(false);
				if (type == DEADLINE) {
					DateTime oldDt = (*it).getDeadline();
					oldDt = swapDtFields(oldDt, dt, param);
					(*it).setDeadline(oldDt);
					(*it).setLastUpdated(dtUpdated);
					writeAllToFile();
					return Status(Status::OK);
				} else if (type == START) {
					DateTime oldDt = (*it).getStart();
					oldDt = swapDtFields(oldDt, dt, param);
					(*it).setStart(oldDt);
					(*it).setLastUpdated(dtUpdated);
					writeAllToFile();
					return Status(Status::OK);
				} else if (type == END) {
					DateTime oldDt = (*it).getEnd();
					oldDt = swapDtFields(oldDt, dt, param);
					(*it).setEnd(oldDt);
					(*it).setLastUpdated(dtUpdated);
					writeAllToFile();
					return Status(Status::OK);
				} else {
					return Status(Status::ERR_INVALID_COMMAND);
				}
			}
		}
	}
	return Status(Status::ERR_NOT_FOUND);
}

Status DbMgr::editTask(int id, TaskParams param, string str) {
	DateTime dtUpdated;
	dtUpdated.setToNow();
	switch (param) {
	case DESCRIPTION:
	case VENUE:
		break;
	default:
		return Status(Status::ERR_INVALID_COMMAND);
		break;
	}
	for (vector<Task>::iterator it = tasks.begin(); it != tasks.end(); it++) {
		if ((*it).getId() == id) {
			if ((*it).getDeleted() == false) {
				if (param == DESCRIPTION) {
					(*it).setDescription(str);
					(*it).setLastUpdated(dtUpdated);
					writeAllToFile();
					return Status(Status::OK);
				} else if (param == VENUE) {
					(*it).setVenue(str);
					(*it).setLastUpdated(dtUpdated);
					writeAllToFile();
					return Status(Status::OK);
				} else {
					return Status(Status::ERR_INVALID_COMMAND);
				}
			}
		}
	}
	return Status(Status::ERR_NOT_FOUND);
}

Status DbMgr::editTask(int id, TaskParams param, bool b) {
	DateTime dtUpdated;
	dtUpdated.setToNow();
	switch (param) {
	case DONE:
		break;
	default:
		return Status(Status::ERR_INVALID_COMMAND);
		break;
	}
	for (vector<Task>::iterator it = tasks.begin(); it != tasks.end(); it++) {
		if ((*it).getId() == id) {
			if ((*it).getDeleted() == false) {
				if (param == DONE) {
					switch (b) {
					case true:
						(*it).setDone();
						break;
					case false:
						(*it).setUndone();
						break;
					}
					(*it).setLastUpdated(dtUpdated);
					writeAllToFile();
					return Status(Status::OK);
				}
			}
		}
	}
	return Status(Status::ERR_NOT_FOUND);
}

Status DbMgr::viewTask(int id) {
	Status s;
	for (vector<Task>::iterator it = tasks.begin(); it != tasks.end(); it++) {
		if ((*it).getId() == id) {
			if ((*it).getDeleted() == false) {
				Task x = *it;
				s.code = Status::OK;
				s.tasks.push_back(x);
				return s;
			}
		}
	}
	s.code = Status::ERR_NOT_FOUND;
	return s;
}

Status DbMgr::viewTasks() {
	Status s;
	for (vector<Task>::iterator it = tasks.begin(); it != tasks.end(); it++) {
		if ((*it).getDeleted() == false) {
			Task x = *it;
			s.tasks.push_back(x);
		}
	}
	if (s.tasks.size()) {
		s.code = Status::OK;
	} else {
		s.code = Status::ERR_NO_RECORDS;
	}
	return s;
}

Status DbMgr::deleteTask(int id, bool isPermanent) {
	DateTime dtUpdated;
	dtUpdated.setToNow();
	for (vector<Task>::iterator it = tasks.begin(); it != tasks.end(); it++) {
		if ((*it).getId() == id) {
			if (isPermanent) {
				tasks.erase(it);
				writeAllToFile();
				return Status(Status::OK);
			} else {
				if ((*it).getDeleted() == false) {
					(*it).setDeleted(true);
					(*it).setLastUpdated(dtUpdated);
					writeAllToFile();
					return Status(Status::OK);
				} // if getDeleted
			} // else
		} // if getId
	} // for
	return Status(Status::ERR_NOT_FOUND);
}

Status DbMgr::undeleteTask(int id) {
	DateTime dtUpdated;
	dtUpdated.setToNow();
	for (vector<Task>::iterator it = tasks.begin(); it != tasks.end(); it++) {
		if ((*it).getId() == id) {
			(*it).setDeleted(false);
			(*it).setLastUpdated(dtUpdated);
			writeAllToFile();
			return Status(Status::OK);
		}
	}
	return Status(Status::ERR_NOT_FOUND);
}

int DbMgr::getLargestTaskId() {
	int largest = 0;
	for (unsigned int i = 0; i < tasks.size(); i++) {
		int thisId = tasks[i].getId();
		if (thisId > largest) {
			largest = thisId;
		}
	}
	return largest;
}

int DbMgr::findTaskIdInVector(int id) {
	int i = 0;
	for (vector<Task>::iterator it = tasks.begin(); it != tasks.end(); it++, i++) {
		if ((*it).getId() == id) {
			if ((*it).getDeleted() == false) {
				return i;
			}
		}
	}
	return -1;
}

void DbMgr::writeAllToFile() {
	FileWriter filewriter(FILENAME_TASKS);
	filewriter.writeTasksToFile(tasks);
	return;
}

Status DbMgr::clearTasks() {
	tasks.clear();
	writeAllToFile();
	return Status(Status::OK);
}

Status DbMgr::searchTasks(string searchstring) {
	Status s;
	transform(searchstring.begin(), searchstring.end(), searchstring.begin(), ::tolower);
	for (vector<Task>::iterator it = tasks.begin(); it != tasks.end(); it++) {
		if (!(*it).getDeleted()) {
			string desc = (*it).getDescription();
			transform(desc.begin(), desc.end(), desc.begin(), ::tolower);
			if (desc.find(searchstring) != string::npos) {
				s.tasks.push_back(*it);
			}
		}
	}
	if (s.tasks.size() > 0) {
		s.code = Status::OK;
	} else {
		s.code = Status::ERR_NO_RESULT;
	}
	return s;
}

Status DbMgr::searchTasks(DateTime searchDt) {
	Status s;
	DateTime earlyLimit = searchDt;
	earlyLimit.setHour(0);
	earlyLimit.setMinute(0);
	earlyLimit.setSecond(0);
	DateTime lateLimit = searchDt;
	lateLimit.setHour(0);
	lateLimit.setMinute(0);
	lateLimit.setSecond(0);
	lateLimit.offset(24 * 60 * 60);
	for (vector<Task>::iterator it = tasks.begin(); it != tasks.end(); it++) {
		if ((*it).getDeleted()) {
			continue;
		}
		if ((*it).getTaskType() == Task::DEADLINE_TASK) {
			DateTime deadlineDt = (*it).getDeadline();
			if (deadlineDt > lateLimit) {
				s.tasks.push_back(*it);

			}
		} else if ((*it).getTaskType() == Task::TIMED_TASK) {
			DateTime startDt = (*it).getStart();
			DateTime endDt = (*it).getEnd();
			if (!((startDt > lateLimit) || (endDt < earlyLimit))) {
				s.tasks.push_back(*it);
			}
		}
	}
	if (s.tasks.size() > 0) {
		s.code = Status::OK;
	} else {
		s.code = Status::ERR_NO_RESULT;
	}
	return s;
}

vector<Task>& DbMgr::retrieveAllTasks() {
	return tasks;
}